package examples.jsf.lifecycle.impl;

import examples.jsf.context.FacesContext;
import examples.jsf.event.PhaseListener;
import examples.jsf.lifecycle.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author Bill
 * @version 2013-05-29
 */
public class LifecycleImpl extends Lifecycle {

    private static final Logger LOGGER = LoggerFactory.getLogger(LifecycleImpl.class);

    // The Phase instance for the render() method
    private Phase response = new RenderResponsePhase();

    // The set of Phase instances that are executed by the execute() method
    // in order by the ordinal property of each phase
    private Phase[] phases = {
            null, // ANY_PHASE placeholder, not a real Phase
            new RestoreViewPhase(),
            new ApplyRequestValuesPhase(),
            new ProcessValidationsPhase(),
            new UpdateModelValuesPhase(),
            new InvokeApplicationPhase(),
            response
    };

    // List for registered PhaseListeners
    private List<PhaseListener> listeners = new CopyOnWriteArrayList<PhaseListener>();


    @Override
    public void execute(FacesContext context) {

        if (context == null) {
            throw new NullPointerException("FacesContext missed");
        }

        for (int i = 1, len = phases.length - 1; i < len; i++) {

            if (context.getRenderResponse() || context.getResponseComplete()) {
                break;
            }

            phases[i].doPhase(context, this, listeners.listIterator());
        }

    }

    @Override
    public void render(FacesContext context) {

        if (context == null) {
            throw new NullPointerException();
        }

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("render(" + context + ")") ;
        }

        if (!context.getResponseComplete()) {
            response.doPhase(context, this, listeners.listIterator());
        }

    }

    @Override
    public void addPhaseListener(PhaseListener listener) {

        if (listener == null) {
            throw new NullPointerException("listener missed");
        }

        if (listeners == null) {
            listeners = new CopyOnWriteArrayList<PhaseListener>();
        }

        if (listeners.contains(listener)) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.warn("phase listener duplicated");
            }
        } else {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("addPhaseListener({},{})",
                        listener.getPhaseId().toString(),
                        listener.getClass().getName());
            }
            listeners.add(listener);
        }
    }

}
